#pragma once

#include <memory>
#include <vulkan/vulkan.hpp>
#include "platform/window.h"


namespace vkrender
{
    struct VulkanDeviceContext
    {
    public:
#ifdef NDEBUG
        bool enableValidationLayers = false;
#else
        bool enableValidationLayers = true;
#endif
    public:
        VkInstance instance;
        VkPhysicalDevice physicalDevice;
        VkDevice device;
        VkSurfaceKHR surface;
        VkQueue graphicsQueue;
        VkQueue presentQueue;
        VkDebugUtilsMessengerEXT debugMessenger;
        VkCommandPool commandPool;

    public:
        VulkanDeviceContext(const std::shared_ptr<VulkanWindow>& window);
        ~VulkanDeviceContext();

        QueueFamilyIndices findQueueFamilies(VkPhysicalDevice gpu);
        uint32_t findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter, 
            VkMemoryPropertyFlags properties);
    private:
        void createInstance();
        void setupDebugMessenger();
        void createSurface(const std::shared_ptr<VulkanWindow>& window);
        void pickPhysicalDevice();
        void createLogicalDevice();
        void createCommandPool();

        // layers and extensions needed.
        const std::vector<const char*> validationLayers = { "VK_LAYER_KHRONOS_validation" };
        const std::vector<const char*> deviceExtensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME };

    public:
        // resource creation
        void createImage(const VkImageCreateInfo& imageInfo, VkMemoryPropertyFlags properties,
            VkImage& image, VkDeviceMemory& imageMemory);
        VkImageView createImageView(VkImage image, VkFormat format, uint32_t layerCount = 1);
        void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties,
            VkBuffer& buffer, VkDeviceMemory& memory);
        void createCommandBuffers(uint32_t num_cb, std::vector<VkCommandBuffer>& commandBuffers);

        // single time commands
        VkCommandBuffer beginSingleTimeCommands();
        void endSingleTimeCommands(VkCommandBuffer commandBuffer);
        void copyBuffer(VkBuffer src, VkBuffer dst, VkDeviceSize size);
        void transitionImageLayout(VkImage image, VkFormat format, 
            VkImageLayout oldLayout, VkImageLayout newLayout, 
            uint32_t baseLayer = 0, uint32_t layerCount = 1);
        void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height);
        void blitImage(VkImage src, uint32_t src_width, uint32_t src_height,
            VkImage dst, uint32_t dst_width, uint32_t dst_height, uint32_t layer = 0);
    };
}
